﻿#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include "ngx_c_conf.h"
#include "ngx_macro.h"
#include "ngx_global.h"
#include "ngx_func.h"
#include "ngx_c_socket.h"
#include "ngx_c_memory.h"
#include "ngx_c_lockmutex.h"
/***************************************************************
 * @file       ngx_C_socket_conn.cxx
 * @brief      连接池成员函数()所调用
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/7/29
 **************************************************************/
ngx_connection_s::ngx_connection_s()//构造函数
{
    iCurrsequence = 0;
    pthread_mutex_init(&logicPorcMutex, NULL); //互斥量初始化
}
/***************************************************************
 * @file       ngx_C_socket_conn.cxx
 * @brief      连接池析构函数
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/8/1
 **************************************************************/
ngx_connection_s::~ngx_connection_s()//析构函数
{
    pthread_mutex_destroy(&logicPorcMutex);    //互斥量释放
}
/***************************************************************
 * @file       ngx_C_socket_conn.cxx
 * @brief      分配出去一个连接的时候初始化一些内容,
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/7/29
 **************************************************************/
void ngx_connection_s::GetOneToUse()
{
    ++iCurrsequence;

    fd  = -1;                                         //开始先给-1
    curStat = _PKG_HD_INIT;                           //收包状态处于 初始状态，准备接收数据包头【状态机】
    precvbuf = dataHeadInfo;                          //收包我要先收到这里来，因为我要先收包头，所以收数据的buff直接就是dataHeadInfo
    irecvlen = sizeof(COMM_PKG_HEADER);               //这里指定收数据的长度，这里先要求收包头这么长字节的数据

    precvMemPointer   = NULL;                         //既然没new内存，那自然指向的内存地址先给NULL
    iThrowsendCount   = 0;                            //原子的
    psendMemPointer   = NULL;                         //发送数据头指针记录
    events            = 0;                            //epoll事件先给0 
    lastPingTime      = time(NULL);                   //上次ping的时间

    FloodkickLastTime = 0;                            //Flood攻击上次收到包的时间
    FloodAttackCount  = 0;                            //Flood攻击在该时间内收到包的次数统计
    iSendCount        = 0;                            //发送队列中有的数据条目数，若client只发不收，则可能造成此数过大，依据此数做出踢出处理 
}
/***************************************************************
 * @file       ngx_C_socket_conn.cxx
 * @brief      回收回来一个连接的时候做一些事
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/7/29
 **************************************************************/
void ngx_connection_s::PutOneToFree()
{
    ++iCurrsequence;
    if(precvMemPointer != NULL)
    {
        CMemory::GetInstance()->FreeMemory(precvMemPointer);
        precvMemPointer = NULL;
    }
    if(psendMemPointer != NULL)
    {
        CMemory::GetInstance()->FreeMemory(psendMemPointer);
        psendMemPointer = NULL;
    }
    iThrowsendCount = 0;
}
/***************************************************************
 * @file       ngx_C_socket_conn.cxx
 * @brief      初始化连接池
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/7/29
 **************************************************************/
void CSocekt::initconnection()
{
    lpngx_connection_t p_Conn;
    /*创建一个类对象，用来调用成员函数*/
    CMemory *p_memory = CMemory::GetInstance();
    int ilenconnpool = sizeof(ngx_connection_t);
    /*创建m_worker_connections个连接池*/
    for(int i = 0; i < m_worker_connections; ++i)
    {
        /*分配内存*/
        p_Conn = (lpngx_connection_t)p_memory->AllocMemory(ilenconnpool, true);
        /*通过定位new来调用构造函数*/
        p_Conn = new(p_Conn) ngx_connection_t();
        /*连接池相关的成员变量初始化*/
        p_Conn->GetOneToUse();
        /*将连接池放入容器当中*/
        m_connectionList.push_back(p_Conn);
        /*空闲连接会放在这个list*/
        m_freeconnectionList.push_back(p_Conn);
    }
    /*开始这两个列表一样大*/
    m_free_connection_n = m_total_connection_n = m_connectionList.size();
    return;
}
/***************************************************************
 * @file       ngx_C_socket_conn.cxx
 * @brief      最终回收连接池，释放内存
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/7/29
 **************************************************************/
void CSocekt::clearconnection()
{
    lpngx_connection_t p_Conn;
    CMemory *p_memory = CMemory::GetInstance();
    while(!m_connectionList.empty())
    {
        p_Conn = m_connectionList.front();
        m_connectionList.pop_front(); 
        p_Conn->~ngx_connection_t();
        p_memory->FreeMemory(p_Conn);
    }
}
/***************************************************************
 * @file       ngx_C_socket_conn.cxx
 * @brief      
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/7/29
 **************************************************************/
lpngx_connection_t CSocekt::ngx_get_connection(int isock)
{
    CLock lock(&m_connectionMutex);
    if(!m_freeconnectionList.empty())
    {
        lpngx_connection_t p_Conn = m_freeconnectionList.front(); 
        m_freeconnectionList.pop_front();
        p_Conn->GetOneToUse();
        --m_free_connection_n; 
        p_Conn->fd = isock;
        return p_Conn;
    }

    //走到这里，表示没空闲的连接了，那就考虑重新创建一个连接
    CMemory *p_memory = CMemory::GetInstance();
    lpngx_connection_t p_Conn = (lpngx_connection_t)p_memory->AllocMemory(sizeof(ngx_connection_t),true);
    p_Conn = new(p_Conn) ngx_connection_t();
    p_Conn->GetOneToUse();
    m_connectionList.push_back(p_Conn);
    ++m_total_connection_n;
    p_Conn->fd = isock;
    return p_Conn;
}
/***************************************************************
 * @file       ngx_C_socket_conn.cxx
 * @brief      归还参数pConn所代表的连接到到连接池中
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/7/29
 **************************************************************/
void CSocekt::ngx_free_connection(lpngx_connection_t pConn) 
{
    CLock lock(&m_connectionMutex);
    pConn->PutOneToFree();
    m_freeconnectionList.push_back(pConn);
    ++m_free_connection_n;
    return;
}
/***************************************************************
 * @file       ngx_C_socket_conn.cxx
 * @brief      
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/8/10
 **************************************************************/
void CSocekt::inRecyConnectQueue(lpngx_connection_t pConn)
{
    std::list<lpngx_connection_t>::iterator pos;
    bool iffind = false;

    CLock lock(&m_recyconnqueueMutex); 

    //如下判断防止连接被多次扔到回收站中来
    for(pos = m_recyconnectionList.begin(); pos != m_recyconnectionList.end(); ++pos)
    {
        if((*pos) == pConn)
        {
            iffind = true;
            break;
        }
    }
    if(iffind == true)
    {
        return;
    }
    pConn->inRecyTime = time(NULL);        //记录回收时间
    ++pConn->iCurrsequence;
    m_recyconnectionList.push_back(pConn); //等待ServerRecyConnectionThread线程自会处理 
    ++m_totol_recyconnection_n;            //待释放连接队列大小+1
    --m_onlineUserCount;                   //连入用户数量-1
    return;
}
/***************************************************************
 * @file       ngx_C_socket_conn.cxx
 * @brief      处理连接回收的线程
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/7/29
 **************************************************************/
void* CSocekt::ServerRecyConnectionThread(void* threadData)
{
    ThreadItem *pThread = static_cast<ThreadItem*>(threadData);
    CSocekt *pSocketObj = pThread->_pThis;

    time_t currtime;
    int err;
    std::list<lpngx_connection_t>::iterator pos,posend;
    lpngx_connection_t p_Conn;
    while(1)
    {
        //为简化问题，我们直接每次休息200毫秒
        usleep(200 * 1000);  //单位是微妙,又因为1毫秒=1000微妙，所以 200 *1000 = 200毫秒
        //不管啥情况，先把这个条件成立时该做的动作做了
        if(pSocketObj->m_totol_recyconnection_n > 0)
        {
            currtime = time(NULL);
            err = pthread_mutex_lock(&pSocketObj->m_recyconnqueueMutex);  
            if(err != 0) ngx_log_stderr(err,"CSocekt::ServerRecyConnectionThread()中pthread_mutex_lock()失败，返回的错误码为%d!",err);
lblRRTD:
            pos = pSocketObj->m_recyconnectionList.begin();
            posend = pSocketObj->m_recyconnectionList.end();
            for(; pos != posend; ++pos)
            {
                p_Conn = (*pos);
                if(
                    ((p_Conn->inRecyTime + pSocketObj->m_RecyConnectionWaitTime) > currtime)  && (g_stopEvent == 0) //如果不是要整个系统退出，你可以continue，否则就得要强制释放
                    )
                {
                    continue; //没到释放的时间
                }    
                //到释放的时间了: 
                //......这将来可能还要做一些是否能释放的判断[在我们写完发送数据代码之后吧]，先预留位置
                //我认为，凡是到释放时间的，iThrowsendCount都应该为0；这里我们加点日志判断下
                if(p_Conn->iThrowsendCount > 0)
                {
                    //这确实不应该，打印个日志吧；
                    ngx_log_stderr(0,"CSocekt::ServerRecyConnectionThread()中到释放时间却发现p_Conn.iThrowsendCount!=0，这个不该发生");
                    //其他先暂时啥也不敢，路程继续往下走，继续去释放吧。
                }
                //流程走到这里，表示可以释放，那我们就开始释放
                --pSocketObj->m_totol_recyconnection_n;        //待释放连接队列大小-1
                pSocketObj->m_recyconnectionList.erase(pos);   //迭代器已经失效，但pos所指内容在p_Conn里保存着呢
                pSocketObj->ngx_free_connection(p_Conn);       //归还参数pConn所代表的连接到到连接池中
                goto lblRRTD; 
            }
            err = pthread_mutex_unlock(&pSocketObj->m_recyconnqueueMutex); 
            if(err != 0)
            {
                ngx_log_stderr(err,"CSocekt::ServerRecyConnectionThread()\
                    pthread_mutex_unlock()失败，返回的错误码为%d!",err);
            }
        }
        if(g_stopEvent == 1) //要退出整个程序，那么肯定要先退出这个循环
        {
            if(pSocketObj->m_totol_recyconnection_n > 0)
            {
                //因为要退出，所以就得硬释放了【不管到没到时间，不管有没有其他不 允许释放的需求，都得硬释放】
                err = pthread_mutex_lock(&pSocketObj->m_recyconnqueueMutex);  
                if(err != 0) ngx_log_stderr(err,"CSocekt::ServerRecyConnectionThread()中pthread_mutex_lock2()失败，返回的错误码为%d!",err);
lblRRTD2:
                pos  = pSocketObj->m_recyconnectionList.begin();
                posend = pSocketObj->m_recyconnectionList.end();
                for(; pos != posend; ++pos)
                {
                    p_Conn = (*pos);
                    --pSocketObj->m_totol_recyconnection_n;        //待释放连接队列大小-1
                    pSocketObj->m_recyconnectionList.erase(pos);   //迭代器已经失效，但pos所指内容在p_Conn里保存着呢
                    pSocketObj->ngx_free_connection(p_Conn);       //归还参数pConn所代表的连接到到连接池中
                    goto lblRRTD2; 
                }
                err = pthread_mutex_unlock(&pSocketObj->m_recyconnqueueMutex); 
                if(err != 0)
                {
                    ngx_log_stderr(err,"CSocekt::ServerRecyConnectionThread()\
                        pthread_mutex_unlock2()失败，返回的错误码为%d!",err);
                }
            }
            break;
        }
    }
    return (void*)0;
}
/***************************************************************
 * @file       ngx_C_socket_conn.cxx
 * @brief      
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/8/6
 **************************************************************/
void CSocekt::ngx_close_connection(lpngx_connection_t pConn)
{
    ngx_free_connection(pConn); 
    if(pConn->fd != -1)
    {
        close(pConn->fd);
        pConn->fd = -1;
    }
    return;
}
